﻿using System;
using System.ComponentModel;
using System.Linq;

namespace PhantomMvvmToolkit
{
    /// <summary>
    /// The extension methods for the IPhantomNotifyPropertyChanged interface that allow this toolkit to work.
    /// </summary>
    public static class PhantomNotifyPropertyChangedExtension
    {
        /// <summary>
        /// Called when PropertyChanged needs to be raised.
        /// </summary>
        /// <param name="notifyChanged">The IPhantomNotifyPropertyChanged object.</param>
        /// <param name="propertyName">Name of the property.</param>
        public static void OnPropertyChanged(this IPhantomNotifyPropertyChanged notifyChanged, string propertyName)
        {
            notifyChanged.OnPropertyChanged(propertyName, ChangedEventType.Normal);   
        }

        /// <summary>
        /// Called when PropertyChanged needs to be raised.
        /// </summary>
        /// <param name="notifyChanged">The IPhantomNotifyPropertyChanged object.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="changeType">Type of the change.</param>
        public static void OnPropertyChanged(this IPhantomNotifyPropertyChanged notifyChanged, string propertyName, ChangedEventType changeType)
        {            
            if (changeType != ChangedEventType.IgnoreDelay &&
                DelayEvents.InDelay.Count(x => x.NotifyProperty == notifyChanged) > 0)
            {
                DelayEvents.InDelay.First(x => x.NotifyProperty == notifyChanged).AddEvent(propertyName);
            }
            else
            {
                if (changeType == ChangedEventType.CrossThreadSafe ||
                    ThreadSafeEvents.InThreadSafe.Count(x => x.SafeObject == notifyChanged) > 0)
                {
                    ThreadSafeEvents.UiThreadDispatcher.BeginInvoke(new Action(() => { notifyChanged.InvokePropertyChanged(new PropertyChangedEventArgs(propertyName)); }));
                }
                else
                    notifyChanged.InvokePropertyChanged(new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Delays the PropertyChanged and CanExecuteChanged events until the returned object is disposed.  This will also seek down the provided 
        /// object's properties and also do the same effect on any IPhantomNotifyPropertyChanged or IPhantomCommand properties it finds.
        /// </summary>
        /// <param name="notifyChanged">The IPhantomNotifyPropertyChanged object.</param>
        /// <returns>A DelayEvents that needs to be disposed of to release the event delay.</returns>
        public static DelayEvents DelayNotifyEvents(this IPhantomNotifyPropertyChanged notifyChanged)
        {
            return notifyChanged.DelayNotifyEvents(false);
        }

        /// <summary>
        /// Delays the PropertyChanged and CanExecuteChanged events until the returned object is disposed.
        /// </summary>
        /// <param name="notifyChanged">The IPhantomNotifyPropertyChanged object.</param>
        /// <param name="onlyRootObject">If set to <c>true</c> this will only delay the provided object.</param>
        /// <returns>A DelayEvents that needs to be disposed of to release the event delay.</returns>
        public static DelayEvents DelayNotifyEvents(this IPhantomNotifyPropertyChanged notifyChanged, bool onlyRootObject)
        {
            DelayEvents retVal;

            if (DelayEvents.InDelay.Count(x => x.NotifyProperty == notifyChanged) > 0)
                retVal = DelayEvents.InDelay.First(x => x.NotifyProperty == notifyChanged);
            else
                retVal = new DelayEvents(notifyChanged, onlyRootObject);

            return retVal;
        }

        /// <summary>
        /// Causes all PropertyChanged and CanExecuteChanged events to be thread safe until this object is disposed.  This will also seek down
        /// the provided object's properties and also do the same effect on any IPhantomNotifyPropertyChanged or IPhantomCommand properties it finds.
        /// </summary>
        /// <param name="notifyChanged">The IPhantomNotifyPropertyChanged object.</param>
        /// <returns>A ThreadSafeEvents that needs to be disposed of to stop sending event raising to the dispatcher.</returns>
        public static ThreadSafeEvents ThreadSafeNotifyEvents(this IPhantomNotifyPropertyChanged notifyChanged)
        {
            return notifyChanged.ThreadSafeNotifyEvents(false);
        }

        /// <summary>
        /// Causes all PropertyChanged and CanExecuteChanged events to be thread safe until this object is disposed.  
        /// </summary>
        /// <param name="notifyChanged">The IPhantomNotifyPropertyChanged object.</param>
        /// <param name="onlyRootObject">If set to <c>true</c> only the root object will be Cross Thread Safe.</param>
        /// <returns>A ThreadSafeEvents that needs to be disposed of to stop sending event raising to the dispatcher.</returns>
        public static ThreadSafeEvents ThreadSafeNotifyEvents(this IPhantomNotifyPropertyChanged notifyChanged, bool onlyRootObject)
        {
            ThreadSafeEvents retVal;

            if (ThreadSafeEvents.InThreadSafe.Count(x => x.SafeObject == notifyChanged) > 0)
                retVal = ThreadSafeEvents.InThreadSafe.First(x => x.SafeObject == notifyChanged);
            else
                retVal = new ThreadSafeEvents(notifyChanged, onlyRootObject);

            return retVal;
        }
    }
}
